// 2015年版权所有etcd作者
// 
// 根据Apache许可证2.0版（以下简称“许可证”）获得许可；
// 除非遵守许可证，否则不得使用此文件。
// 您可以通过
// 
// www.apache.org/licenses/LICENSE-2.0 
// 
// 除非适用法律要求或书面同意，否则根据许可证分发的软件
// 按“原样”分发，
// 无任何明示或暗示的保证或条件。
// 请参阅许可证，了解管理许可的特定语言和
// 许可证下的限制。

// 开始：构建！windows&&！计划9 
// /+建造！窗户，！plan9 

package osutil

import (
	"os"
	"os/signal"
	"sync"
	"syscall"

	"go.uber.org/zap"
)

// InterruptHandler是在接收
// SIGTERM或SIGINT信号时调用的函数。
type InterruptHandler func()

var (
	interruptRegisterMu, interruptExitMu sync.Mutex
	// interruptHandlers按顺序保存所有注册的interruptHandlers 
	// 它们将被执行。
	interruptHandlers = []InterruptHandler{}
)

// RegisterInterruptHandler注册一个新的InterruptHandler。启动中断处理后注册的处理程序
// 将不会执行。
func RegisterInterruptHandler(h InterruptHandler) {
	interruptRegisterMu.Lock()
	defer interruptRegisterMu.Unlock()
	interruptHandlers = append(interruptHandlers, h)
}

// HandleInterrupts在接收到SIGINT或SIGTERM时调用处理函数。
func HandleInterrupts(lg *zap.Logger) {
	notifier := make(chan os.Signal, 1)
	signal.Notify(notifier, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		sig := <-notifier

		interruptRegisterMu.Lock()
		ihs := make([]InterruptHandler, len(interruptHandlers))
		copy(ihs, interruptHandlers)
		interruptRegisterMu.Unlock()

		interruptExitMu.Lock()

		if lg != nil {
			lg.Info("received signal; shutting down", zap.String("signal", sig.String()))
		}

		for _, h := range ihs {
			h()
		}
		signal.Stop(notifier)
		pid := syscall.Getpid()
		// 如果是“init”进程，则直接退出，因为内核不会帮助杀死pid 1。
		if pid == 1 {
			os.Exit(0)
		}
		setDflSignal(sig.(syscall.Signal))
		syscall.Kill(pid, sig.(syscall.Signal))
	}()
}

// 将继电器退出操作系统。如果没有中断处理程序正在运行，则退出，否则阻塞。
func Exit(code int) {
	interruptExitMu.Lock()
	os.Exit(code)
}
